package com.scheduler.backend.aps.deadlinefirst;

import com.scheduler.backend.aps.domain.ResourceGroup;
import com.scheduler.backend.aps.domain.deadlinefirst.OrderSlice;
import com.scheduler.backend.aps.domain.deadlinefirst.ScheduleTable;
import com.scheduler.backend.data.repository.OrderSliceRepository;
import com.scheduler.backend.data.repository.OrderSliceToEmployeeGroupRepository;
import com.scheduler.backend.po.OrderSlicePO;
import com.scheduler.backend.po.OrderSliceToEmployeeGroup;
import lombok.extern.slf4j.Slf4j;
import org.optaplanner.core.api.solver.SolverJob;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.api.solver.SolverStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.ExecutionException;

/**
 * @author heimingz
 */
@Slf4j
@Component
public class DeadlineFirstScheduler {
    private final SolverManager<ScheduleTable, UUID> solverManager;
    private final OrderSliceRepository orderSliceRepo;
    private final OrderSliceToEmployeeGroupRepository orderSliceToEmployeeGroupRepo;

    @Autowired
    public DeadlineFirstScheduler(SolverManager<ScheduleTable, UUID> solverManager,
                                  OrderSliceRepository orderSliceRepo,
                                  OrderSliceToEmployeeGroupRepository orderSliceToEmployeeGroupRepo) {
        this.solverManager = solverManager;
        this.orderSliceRepo = orderSliceRepo;
        this.orderSliceToEmployeeGroupRepo = orderSliceToEmployeeGroupRepo;
    }

    /**
     * 求解并等待结果
     *
     * @param problemId     问题唯一id
     * @param scheduleTable 问题输入
     * @return 问题结果
     */
    public ScheduleTable solveAndWait(UUID problemId, ScheduleTable scheduleTable) {
        SolverJob<ScheduleTable, UUID> solverJob = solverManager.solve(problemId, scheduleTable);
        ScheduleTable solution;
        try {
            // Wait until the solving ends
            solution = solverJob.getFinalBestSolution();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Solving failed.", e);
        }
        return solution;
    }

    /**
     * 求解并保存
     *
     * @param problemId     问题唯一id
     * @param scheduleTable 问题输入
     */
    public void solveAndSave(UUID problemId, ScheduleTable scheduleTable) {
        SolverJob<ScheduleTable, UUID> solverJob = solverManager.solve(problemId, scheduleTable);
        ScheduleTable solution;
        try {
            // Wait until the solving ends
            solution = solverJob.getFinalBestSolution();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Solving failed.", e);
        }

        for (OrderSlice orderSlice : solution.getOrderSlices()) {
            orderSliceRepo.save(new OrderSlicePO(orderSlice, ScheduleTable.ORDER_SLICE_TIMESLOT_SPAN));
            for (ResourceGroup group : orderSlice.getEmployees().getEmployeeGroups()) {
                orderSliceToEmployeeGroupRepo.save(new OrderSliceToEmployeeGroup(orderSlice.getId(), group.getResourceId()));
            }
        }
    }

    /**
     * 求解并保存
     *
     * @param problemId     问题唯一id
     * @param scheduleTable 问题输入
     */
    public SolverJob solveAndSave2(UUID problemId, ScheduleTable scheduleTable) {
        return solverManager.solve(problemId, scheduleTable, s -> {
            for (OrderSlice orderSlice : s.getOrderSlices()) {
                orderSliceRepo.save(new OrderSlicePO(orderSlice, ScheduleTable.ORDER_SLICE_TIMESLOT_SPAN));
                for (ResourceGroup group : orderSlice.getEmployees().getEmployeeGroups()) {
                    orderSliceToEmployeeGroupRepo.save(new OrderSliceToEmployeeGroup(orderSlice.getId(), group.getResourceId()));
                }
            }
        });
    }

    /**
     * 查看求解状态
     *
     * @param problemId 问题唯一id
     * @return 求解状态
     */
    public SolverStatus getSolverStatus(UUID problemId) {
        return solverManager.getSolverStatus(problemId);
    }

    /**
     * 提前结束求解
     *
     * @param problemId 问题唯一id
     */
    public void terminateEarly(UUID problemId) {
        solverManager.terminateEarly(problemId);
    }

    private ScheduleTable getScheduleTable(UUID problemId) {
        return null;
    }

    private void save(ScheduleTable scheduleTable) {
        log.error(scheduleTable.toString());
    }
}